package com.rw.engine.sign.aspect;

import com.rw.engine.sign.anno.SignLimit;
import com.rw.engine.sign.anno.SignParam;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;

import javax.annotation.Resource;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 签名切片
 */
@Aspect
public class SignAspect {
    /**
     * Spring容器
     * 我们的过滤规则需要从Spring容器里面拿取
     * 以确保用户可以自定义过滤规则
     */
    @Resource
    protected ApplicationContext applicationContext;

    @Around("@annotation(com.rw.engine.sign.anno.SignLimit)")
    public Object signCheck(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法的对象
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        // 获取方法上，所有参数的注解
        Annotation[][] annotations = method.getParameterAnnotations();
        // 获取所有传入的参数
        Object[] args = joinPoint.getArgs();
        // 获取所有拥有 @SignParam 的参数
        Map<SignParam, Object> objectMap = getObjectMap(annotations, args);

        // 获取 SignRule 签名校验规则，遍历所有规则
        SignLimit signLimit = method.getAnnotation(SignLimit.class);
        for (Class<? extends SignRule> signRuleClass : signLimit.signFilterRule()) {
            // 校验
            SignRule signRule = findFilterRule(signRuleClass);
            signRule.check(objectMap);
        }

        // 执行原始方法
        return joinPoint.proceed();
    }

    /**
     * 获取所有拥有 @SignParam 的参数
     *
     * @param annotations 注解数组
     * @param args        参数数组
     * @return 所有拥有 @SignParam 的参数
     */
    private Map<SignParam, Object> getObjectMap(Annotation[][] annotations, Object[] args) {
        // 遍历方法参数和对应的注解数组，找到包含指定注解的参数
        Map<SignParam, Object> objectMap = new HashMap<>();
        for (int i = 0; i < annotations.length; i++) {
            // 该参数被 @SignParam 标注
            Optional<SignParam> optional = containsSignParam(annotations[i]);
            // 如果存在，则把注解和参数保存起来
            if (optional.isPresent()) {
                objectMap.put(optional.get(), args[i]);
            }
        }
        return objectMap;
    }

    /**
     * 遍历注解列表，查找是否存在注解 @SignParam
     */
    private Optional<SignParam> containsSignParam(Annotation[] annotations) {
        return Arrays.stream(annotations)
                .filter(annotation -> annotation instanceof SignParam)
                .map(annotation -> (SignParam) annotation)
                .findAny();
    }

    /**
     * 尝试从Spring容器里取出过滤规则
     * 如果取出失败，则构建一个对象
     *
     * @return 取出或构建的对象
     */
    private <CustomFilterRule> CustomFilterRule findFilterRule(Class<CustomFilterRule> filterRuleClass)
            throws InstantiationException, IllegalAccessException {
        try {
            return applicationContext.getBean(filterRuleClass);
        } catch (BeansException exception) {
            return filterRuleClass.newInstance();
        }
    }
}
